import json
import os
import stat
import sys
import datetime
import zipfile
from typing import Dict, List

from PySide2.QtGui import QColor
from PySide2.QtWidgets import *
from PySide2.QtCore import Qt, QRect, QObject
from PySide2.QtWidgets import QFileDialog
import win32api  # pip install pypiwin32

import JsonGenerator

import Logger

import ExcelReader
import SvnTool
from UIMain import Ui_MainWindow
import Util
from dialog.PublishDialog import PublishDialog
from dialog.CoderDialog import CoderDialog
from ExcelData import ExcelData, TableData, EData, SaveType, JsonListVersionData, SyncPublishServer

# from PublishDialog import PublishDialog
from dialog.SettingDialog import SettingDialog
from LogDialog import LogDialog
from dialog.TreeViewDialog import TreeViewDialog
from dialog.ViewerDialog import ViewerDialog
from lib import Publish, CompatibleCRC32 as CRC32
from lib.ExcelLibDefinitionMain import ExcelLibDefinitionMain
from lib.ExcelLibDefinitionTable import ExcelLibDefinitionTable

COL_XLS: [] = ["文件名", "表", "最后刷新日期"]
COL_XLS_WDTH: [] = [180, 30, 160]
COL_XLS_FILENAME: int = 0
COL_XLS_TABLE_COUNT: int = 1
COL_XLS_LAST_DATE: int = 2

COL_TB: [] = ["JSON文件名", "表名", "状态", "最后更新"]
COL_TB_WDTH: [] = [180, 120, 40, 160]
COL_TB_JSON_NAME: int = 0
COL_TB_NAME: int = 1
COL_TB_STATUS: int = 2
COL_TB_LAST_DATE: int = 3

COL_VER: [] = ["状态", "时间", "版本", "数", "生成者", "CRC32", "操作日期", "短路径", "目录", "程序", "中心", "SVN",
               "路径"]
COL_VER_WDTH: [] = [80, 90, 150, 30, 160, 85, 160, 272, 51, 51, 51, 51, 1000]
COL_VER_TAG: int = 0
COL_VER_LAST_DATE: int = 1
COL_VER_VERSION: int = 2
COL_VER_COUNT: int = 3
COL_VER_AUTHOR: int = 4
COL_VER_MD5: int = 5
COL_VER_DATE: int = 6
COL_VER_SHORT_PATH: int = 7
COL_VER_OPEN_DIR: int = 8
COL_VER_SYNC: int = 9
COL_VER_PUBLISH: int = 10
COL_VER_SVN: int = 11
COL_VER_PATH: int = 12

COL_JSON: [] = ["状态", "文件名", "生成者", "CRC32", "导出日期", "查看", "比较", "编辑", "删除", ""]
COL_JSON_WDTH: [] = [80, 170, 120, 85, 160, 51, 51, 51, 51, 99]
COL_JSON_TAG: int = 0
COL_JSON_FILENAME: int = 1
COL_JSON_AUTHOR: int = 2
COL_JSON_MD5: int = 3
COL_JSON_DATE: int = 4
COL_JSON_FUN_VIEW: int = 5
COL_JSON_FUN_COMPARE: int = 6
COL_JSON_FUN_EDIT: int = 7
COL_JSON_FUN_DELETE: int = 8

VERSION_CAN_NOT_FOUND_DATA: str = "暂无生成"
VERSION_NEVER_GENERATE_VERSION: str = "本地未生成过版本"

VER_TAG_DUPLICATE: str = "重复"
VER_TAG_NEWEST: str = "最新生成"


class Excel2JsonWin(QMainWindow, Ui_MainWindow):
    edata: EData
    """软件核心数据，记录使用什么excel表等信息"""

    listFile: QTableWidget
    selectedExcelData: ExcelData = None

    logger: Logger

    currentTreeShowFile: str = ""
    """当前树正在展示的文件"""

    settingDialog: SettingDialog
    publishDialog: PublishDialog
    coderDialog: CoderDialog
    # logDialog: LogDialog
    treeDialog: TreeViewDialog
    viewerDialog: ViewerDialog

    btnDirs: List[QPushButton]

    def __init__(self):
        super(Excel2JsonWin, self).__init__()

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)  # 加载窗体，但是还未显示出来
        Util.mainForm = self

        # 引入3个弹出窗体
        self.settingDialog = SettingDialog()
        self.publishDialog = PublishDialog()
        self.coderDialog = CoderDialog()
        # self.logDialog = LogDialog()
        self.treeDialog = TreeViewDialog()
        self.viewerDialog = ViewerDialog()

        self.logger = Logger.Logger()
        self.logger.bindOuttext(self.ui.txtLog)

        # 注册菜单点击事件
        self.ui.menuOpen.triggered.connect(self.onBtnOpenFileClick)
        self.ui.menuSetting.triggered.connect(self.onMenuSettingClick)  # 配置菜单
        self.ui.menuQuit.triggered.connect(self.onQuitClick)
        self.ui.menuCoder.triggered.connect(self.onMenuCoderClick)  # 代码生成菜单
        self.ui.menuGitee.triggered.connect(self.onMenuGiteeClick)

        # 注册UI按钮绑定事件
        self.ui.btnOpenFile.clicked.connect(self.onBtnOpenFileClick)  # 导入excel
        self.ui.btnSave.clicked.connect(self.onBtnSaveClick)  # 保存列表
        # self.ui.btnExport.clicked.connect(self.onBtnExportClick)  # 选中导出json
        self.ui.btnSelectAll.clicked.connect(self.onBtnSelectAllClick)  # 全选
        self.ui.btnSelectNone.clicked.connect(self.onBtnSelectNoneClick)  # 全不选
        # self.ui.btnDelete.clicked.connect(self.onBtnDeleteClick)  # 删除选中
        self.ui.btnClear.clicked.connect(self.onBtnClearClick)  # 全部删除对象，等于新建
        self.ui.btnRelopenFile.clicked.connect(self.onBtnRelopenFileClick)  # 重新读取选中的表
        self.ui.btnRelopenAllFile.clicked.connect(self.onBtnRelopenAllFileClick)  # 重新读取所有的表

        self.ui.btnMenuCoder.clicked.connect(self.onMenuCoderClick)  # 代码生成菜单

        self.ui.btnModifyTag.clicked.connect(self.onModifyTagClick)  # 修改标签
        self.ui.btnOpenExcelDir.clicked.connect(self.onOpenExcelDirClick)  # 打开当前环境目录

        self.ui.btnAddUseDir.clicked.connect(self.onAddUseDirClick)
        self.ui.btnRemoveUseDir.clicked.connect(self.onRemoveUseDirClick)

        self.ui.btnMoveUp1.clicked.connect(self.onMoveUp1Click)
        self.ui.btnMoveDown1.clicked.connect(self.onMoveDown1Click)

        # self.ui.btnOpenLog.clicked.connect(self.onBtnOpenLogClick)  # 日志窗口
        # self.ui.btnMenuSetting.clicked.connect(self.onMenuSettingClick)  # 配置菜单

        self.ui.listFile.itemClicked.connect(self.onListFileClick)  # 点击列表Item事件
        self.ui.listExcel.itemClicked.connect(self.onListExcelClick)  # 点击列表Item事件

        # 绑定事件
        self.ui.btnGenJson.clicked.connect(self.onBtnGenJsonClick)
        self.ui.btnGenJsonAll.clicked.connect(self.onBtnGenJsonAllClick)
        self.ui.btnUpdateJson.clicked.connect(self.onBtnUpdateJsonClick)
        # self.ui.btnCommitJson.clicked.connect(self.onBtnCommitJsonClick)

        self.ui.listJson.itemClicked.connect(self.onListJsonClick)  # 点击列表Item事件
        self.ui.listVersion.itemClicked.connect(self.onListVersionClick)  # 点击列表Item事件

        # 设置表头样式
        Util.initColumnListFile(self.ui.listExcel, COL_XLS, COL_XLS_WDTH, Util.HEADER_STYLE_BLUE)
        Util.initColumnListFile(self.ui.listFile, COL_TB, COL_TB_WDTH, Util.HEADER_STYLE_BLUE)
        Util.initColumnListFile(self.ui.listJson, COL_JSON, COL_JSON_WDTH, Util.HEADER_STYLE_YELLOW)
        Util.initColumnListFile(self.ui.listVersion, COL_VER, COL_VER_WDTH, Util.HEADER_STYLE_YELLOW)

        # self.logDialog.show()
        # 从记录文件读取之前的列表
        self.edata = EData()
        self.edata.restoreFromFile()

        # 读取两个勾选框
        self.ui.chkAutoOpenFolderGenJson.setChecked(self.edata.setting.chkAutoOpenFolderGenJson)
        self.ui.chkAutoOpenFolderCopy.setChecked(self.edata.setting.chkAutoOpenFolderCopy)
        self.ui.chkAutoCopyGenJson.setChecked(self.edata.setting.chkAutoCopyGenJson)
        self.ui.chkZip.setChecked(self.edata.setting.chkZip)

        self.ui.chkAutoOpenFolderGenJson.clicked.connect(self.onChkAutoOpenFolderGenJsonClick)
        self.ui.chkAutoOpenFolderCopy.clicked.connect(self.onChkAutoOpenFolderCopyClick)
        self.ui.chkAutoCopyGenJson.clicked.connect(self.onChkAutoCopyGenJsonClick)
        self.ui.chkZip.clicked.connect(self.onChkZipClick)

        # 绑定日志按钮
        self.ui.btnClearLogs.clicked.connect(self.onBtnClearLogsClick)  # 全部删除
        self.ui.btnEndLogs.clicked.connect(self.onBtnEndLogs)
        self.ui.btnSaveLogs.clicked.connect(self.onBtnSaveLogs)

        # 创建未建立目录
        jsonBasePath: str = self.getGenJsonBasePath()
        Util.tryCreateDirectory(jsonBasePath)
        historyBasePath: str = Util.fixPathSlash(jsonBasePath + self.edata.setting.svnHistoryFolder, True)
        Util.tryCreateDirectory(historyBasePath)

        # self.ui.frmDir.setWordWrap(True)

        self.btnDirs = list()
        self.loadDirButtons()

        # 刷新目录按钮名字
        self.refreshDirTags()
        # 修正当前选择空间不能超
        if self.edata.excelDatas.currUseExcelDataOrd >= self.edata.excelDatas.dataCount:
            self.edata.excelDatas.currUseExcelDataOrd = self.edata.excelDatas.dataCount - 1
        # 启动默认选择的操作空间
        self.selectDir(self.edata.excelDatas.currUseExcelDataOrd)

    """下面是Excel空间控制功能"""

    def loadDirButtons(self):
        # layout = self.ui.frmDir.layout()
        for i in range(0, self.edata.excelDatas.dataCount):
            self.addDirButton(i, self.edata.excelDatas.dataFiles[i].tag)

    def addDirButton(self, ord, name):
        layout = self.ui.frmDir.layout()
        layout.setAlignment(Qt.AlignLeft)
        layout.setSpacing(1)
        btn = QPushButton("", self.ui.frmDir)
        btn.setText(name)
        btn.setCheckable(True)
        btn.setMaximumSize(200, 30)
        btn.clicked.connect(self.onBtnUseDirClick)
        btn.setStatusTip(str(ord))
        self.btnDirs.append(btn)
        layout.addWidget(btn)

    def removeDirButton(self, ord):
        layout = self.ui.frmDir.layout()
        btn = self.btnDirs.pop(ord)
        layout.removeWidget(btn)
        btn.deleteLater()
        for i in range(0, len(self.btnDirs)):
            self.btnDirs[i].setStatusTip(str(i))

    def onAddUseDirClick(self):
        """增加空间"""
        if (self.edata.excelDatas.isDataLimitReached == False):
            dataFile, ord = self.edata.excelDatas.addOneData()
            if dataFile != None:
                self.addDirButton(ord, dataFile.tag)
                self.edata.save(SaveType.ExcelAnalysis)
            else:
                QMessageBox.warning(self, "提示", "添加错误")

    def onRemoveUseDirClick(self):
        """删除空间"""
        if (self.edata.excelDatas.dataCount > 1):
            choice = QMessageBox.question(self, '确认', '['
                                          + self.edata.excelDatas.selectedExcel.tag
                                          + '] \n是否删除该空间？\n数据将会丢失“',
                                          QMessageBox.Yes | QMessageBox.No,
                                          QMessageBox.Yes)
            if choice == QMessageBox.Yes:
                ord = self.edata.excelDatas.currUseExcelDataOrd
                self.edata.excelDatas.removeOneData(ord)
                self.edata.save(SaveType.ExcelAnalysis)
                self.removeDirButton(ord)
                self.selectDir(0)
        else:
            QMessageBox.warning(self, "提示", "最少保留1个数据空间")

    def onMoveUp1Click(self):
        """文件排序上移"""
        swapA = self.ui.listExcel.currentRow()
        if self.ui.listExcel.currentRow() < 0:
            QMessageBox.warning(self, "提示", "你还没有选择要调整顺序的Excel文件！")
            return
        swapB = swapA - 1
        if swapB < 0:
            return
        self.edata.excelDatas.selectedExcel.swapDataItems(swapA, swapB)
        selectItem: QTableWidgetItem = self.ui.listExcel.item(swapB, COL_XLS_FILENAME)
        self.ui.listExcel.setCurrentItem(selectItem)
        # self.edata.save(SaveType.ExcelAnalysis)
        # 刷新列表
        self.refreshExcelList()

    def onMoveDown1Click(self):
        """文件排序下移"""
        swapA = self.ui.listExcel.currentRow()
        if self.ui.listExcel.currentRow() < 0:
            QMessageBox.warning(self, "提示", "你还没有选择要调整顺序的Excel文件！")
            return
        swapB = swapA + 1
        if swapB > len(self.edata.excelDatas.selectedDatas) - 1:
            return
        self.edata.excelDatas.selectedExcel.swapDataItems(swapA, swapB)
        selectItem: QTableWidgetItem = self.ui.listExcel.item(swapB, COL_XLS_FILENAME)
        self.ui.listExcel.setCurrentItem(selectItem)
        # self.edata.save(SaveType.ExcelAnalysis)
        # 刷新列表
        self.refreshExcelList()

    def refreshSelectedDir(self):
        path = self.edata.excelDatas.selectedExcel.fileDir
        if len(path) < 1:
            path = "(当前空间未使用)"
            self.ui.btnOpenExcelDir.setEnabled(False)
            self.ui.btnMenuCoder.setEnabled(False)
            self.ui.btnGenJson.setEnabled(False)
            self.ui.btnGenJsonAll.setEnabled(False)
        else:
            self.ui.btnOpenExcelDir.setEnabled(True)
            self.ui.btnMenuCoder.setEnabled(True)
            self.ui.btnGenJson.setEnabled(True)
            self.ui.btnGenJsonAll.setEnabled(True)
        self.setWindowTitle("当前环境空间：" + path)

    def refreshDirTags(self):
        for i in range(len(self.btnDirs)):
            self.btnDirs[i].setText(self.edata.excelDatas.files[i].tag)

    def onBtnUseDirClick(self):
        """点击环境按钮"""
        sender = self.sender()
        ord = int(sender.statusTip())
        # print(ord)
        self.selectDir(ord)

    def selectDir(self, ord):
        for i in range(len(self.btnDirs)):
            self.btnDirs[i].setChecked(False)
        self.btnDirs[ord].setChecked(True)

        self.edata.excelDatas.currUseExcelDataOrd = ord

        self.refreshSelectedDir()  # 刷新选择目录
        self.refreshExcelList()
        self.clearListFile()
        self.showFirstExcelRow()
        # self.edata.save(SaveType.ExcelAnalysis)

    def onModifyTagClick(self):
        """修改标签"""

        # 创建一个文本输入框
        text, ok = QInputDialog.getText(self, '输入框', '请输入环境标签：')

        # 如果用户点击了确定按钮
        if ok:
            # 获取输入框中的文本
            text = text.strip()
            if (len(text) > 0):
                self.edata.excelDatas.selectedExcel.tag = text
        else:
            # 如果用户点击了取消按钮
            return None

        self.refreshDirTags()  # 刷新标签名
        self.edata.save(SaveType.ExcelAnalysis)

    def onOpenExcelDirClick(self):
        """打开当前环境目录"""

        path = self.edata.excelDatas.selectedExcel.fileDir
        if (len(path) > 0):
            Util.openFilePath(path)

    # def refreshUseDir(self):
    #     # for i in range(len(self.btnDirs)):
    #     #     if (i <= self.edata.excelDatas.currUseExcelDataCount):
    #     #         self.btnDirs[i].setVisible(True)
    #     #     else:
    #     #         self.btnDirs[i].setVisible(False)
    #     if (self.edata.excelDatas.currUseExcelDataOrd > self.edata.excelDatas.currUseExcelDataCount):
    #         self.selectDir(0)

    """下面是正式功能"""

    def showEvent(self, event):
        self.logger.logNotice("软件运行目录：" + self.edata.currentPath, self)
        self.refreshExcelList()
        self.refreshTableWidgetJson()
        screen: QRect = Util.getScreenRect()
        y: int = int((screen.height() - (self.geometry().height() + 300)) / 2)
        if y < 0:
            y = 0
        self.move(self.geometry().x(), y)

    # def resizeEvent(self, event):
    #     self.logger.resetFormPosition(self)

    # def moveEvent(self, event):
    #     self.logger.resetFormPosition(self)

    def closeEvent(self, event):
        sys.exit(0)

    def onChkAutoOpenFolderGenJsonClick(self):
        """点击checkbox生成json后打开目录"""
        self.edata.setting.chkAutoOpenFolderGenJson = self.ui.chkAutoOpenFolderGenJson.isChecked()
        self.edata.save(SaveType.Setting)

    def onChkAutoOpenFolderCopyClick(self):
        """点击checkbox同步拷贝json后打开目录"""
        self.edata.setting.chkAutoOpenFolderCopy = self.ui.chkAutoOpenFolderCopy.isChecked()
        self.edata.save(SaveType.Setting)

    def onChkAutoCopyGenJsonClick(self):
        """点击checkbox生成json后同步拷贝json到指定位置"""
        self.edata.setting.chkAutoCopyGenJson = self.ui.chkAutoCopyGenJson.isChecked()
        self.edata.save(SaveType.Setting)

    def onChkZipClick(self):
        """点击记录是否使用ZIP压缩"""
        self.edata.setting.chkZip = self.ui.chkZip.isChecked()
        self.edata.save(SaveType.Setting)

    def onQuitClick(self):
        sys.exit(0)

    def onMenuSettingClick(self):
        self.settingDialog.show()

    def onMenuCoderClick(self):
        self.syncCheckStateToExcelData()  # 同步勾选状态
        self.coderDialog.show()

    def onBtnClearLogsClick(self):
        """清除控制台内容"""
        self.logger.clear()

    def onBtnEndLogs(self):
        """滚到屏底"""
        pass

    def onBtnSaveLogs(self):
        """保存日志"""
        pass

    # def onBtnOpenLogClick(self):
    #     self.logDialog.reShow()

    def onBtnDeleteClick(self):
        """删除选中"""
        self.ui.listExcel.currentRow()
        if self.ui.listExcel.currentRow() < 0:
            QMessageBox.warning(self, "提示", "你还没有选择要删除的Excel文件！")
            return
        xlsItem: QTableWidgetItem = self.ui.listExcel.item(self.ui.listExcel.currentRow(), COL_XLS_FILENAME)
        del self.edata.excelDatas.selectedDatas[xlsItem.text()]

        # 刷新列表
        self.refreshExcelList()
        self.clearListFile()

    def onBtnClearClick(self):
        """全部删除"""
        self.edata.excelDatas.selectedExcel.clear()
        self.refreshExcelList()
        self.clearListFile()

    def clearListFile(self):
        self.ui.listFile.setRowCount(0)
        self.ui.listFile.clear()
        Util.initColumnListFile(self.ui.listFile, COL_TB, COL_TB_WDTH, Util.HEADER_STYLE_BLUE)
        self.refreshSelectedDir()

    def onBtnRelopenAllFileClick(self):
        """重新加载所有文件"""
        self.logger.logNotice("开始重新加载所有excel")
        isSuccess: bool = False
        count: int = 0
        for xls in self.edata.excelDatas.selectedDatas.values():
            self.logger.logNotice("开始重新加载:" + xls.fileName)
            isSuccess = self.reloadFile(xls.fileName)
            if isSuccess:
                self.logger.logNotice("重新加载[ " + xls.fileName + "] 成功！")
            else:
                self.logger.logError("重新加载[ " + xls.fileName + "] 失败！中断全部重新加载！")
                return
            count += 1
        if isSuccess:
            self.onBtnSelectAllClick()  # 全选
            choice = QMessageBox.question(self, '确认', '导入解析文件成功！\n是否立刻保存列表?“',
                                          QMessageBox.Yes | QMessageBox.No,
                                          QMessageBox.Yes)
            if choice == QMessageBox.Yes:
                self.onBtnSaveClick()

    def onBtnRelopenFileClick(self):
        """重新加载文件"""
        self.ui.listExcel.currentRow()
        if self.ui.listExcel.currentRow() < 0:
            QMessageBox.warning(self, "提示", "你还没有选择要刷新的Excel文件！")
            return
        xlsItem: QTableWidgetItem = self.ui.listExcel.item(self.ui.listExcel.currentRow(), COL_XLS_FILENAME)
        isSuccess = self.reloadFile(xlsItem.text())

        if isSuccess:
            self.onBtnSelectAllClick()  # 全选
            choice = QMessageBox.question(self, '确认', '导入解析文件成功！\n是否立刻保存列表?“',
                                          QMessageBox.Yes | QMessageBox.No,
                                          QMessageBox.Yes)
            if choice == QMessageBox.Yes:
                self.onBtnSaveClick()
        return

    def reloadFile(self, fileName: str):
        oldData: ExcelData = self.edata.excelDatas.selectedDatas.get(fileName)
        if not os.path.isfile(oldData.filePath):
            QMessageBox.warning(self, "提示", "你选择的Excel文件，它的原始导入文件已经不存在了！")
            return
        self.logger.logNotice('重新刷新加载Excel文件:' + oldData.fileName, self)
        data: ExcelData = ExcelData()
        data.fileName = oldData.fileName
        data.filePath = oldData.filePath
        isSuccess = ExcelReader.importFile(data)
        if isSuccess:
            print(len(data.tables))
            self.logger.logSuccess('解析文件[' + oldData.fileName + ']结构: 成功！(*^▽^*)', self)
            self.edata.excelDatas.selectedDatas[oldData.fileName] = data
            self.refreshExcelList()
            self.refreshTableList(data.fileName, data.tables)
            self.onBtnSelectAllClick()  # 全选
            self.refreshSelectedDir()  # 刷新所选目录
            return True
        else:
            self.logger.logError('解析文件[' + oldData.fileName + ']结构: 失败！！', self)
            return False

    def onBtnOpenFileClick(self):
        """选择Excel文件"""
        filePaths = QFileDialog.getOpenFileNames(self, "选择引入的Excel配置文件", "./", "Excel Files (*.xls;*.xlsx)")
        # if filePath[0] == "": #没有选择文件，退出
        #     return
        if len(filePaths[0]) < 1:
            return
        dirPath = ""
        successCount = 0
        for path in filePaths[0]:
            print('File:', path)
            if not os.path.isfile(path):
                self.logger.logError('加载Excel文件错误！' + path, self)
                continue
            if dirPath == "":
                dirPath = os.path.dirname(path)
            fileName = os.path.basename(path)
            self.logger.logNotice('加载Excel文件:' + fileName, self)
            data: ExcelData = ExcelData()
            data.fileName = fileName
            data.filePath = path
            isSuccess = ExcelReader.importFile(data)
            if isSuccess:
                print(len(data.tables))
                self.logger.logSuccess('解析文件[' + fileName + ']结构: 成功！(*^▽^*)', self)
                self.edata.excelDatas.selectedDatas[fileName] = data
                self.refreshExcelList()
                self.refreshTableList(data.fileName, data.tables)
                self.onBtnSelectAllClick()  # 全选
                successCount += 1
            else:
                self.logger.logError('解析文件[' + fileName + ']结构: 失败！！', self)
        if successCount > 0:
            choice = QMessageBox.question(self, '确认', '导入解析文件成功！\n是否立刻保存列表?“',
                                          QMessageBox.Yes | QMessageBox.No,
                                          QMessageBox.Yes)
            if choice == QMessageBox.Yes:
                self.edata.excelDatas.selectedExcel.fileDir = dirPath
                self.setWindowTitle(dirPath)
                self.onBtnSaveClick()
                self.refreshSelectedDir()  # 刷新所选目录

    def onBtnSaveClick(self):
        result = self.edata.save(SaveType.ExcelAnalysis)
        if result:
            QMessageBox.information(self, "提示", "已经保存列表!")
        else:
            QMessageBox.warning(self, "提示", "保存列表失败！")

    def onListExcelClick(self, item: QTableWidgetItem):
        """点击excel文件列表"""
        print("Excel文件列表 您点击了：" + item.text() + " row:" + str(item.row()))
        fileItem: QTableWidgetItem = self.ui.listExcel.item(item.row(), COL_XLS_FILENAME)
        fileName: str = fileItem.text()
        data: ExcelData = self.edata.excelDatas.selectedDatas[fileName]
        self.refreshTableList(fileName, data.tables)

    def showFirstExcelRow(self):
        if self.ui.listExcel.rowCount() > 0:
            fileItem: QTableWidgetItem = self.ui.listExcel.item(0, COL_XLS_FILENAME)
            fileName: str = fileItem.text()
            data: ExcelData = self.edata.excelDatas.selectedDatas[fileName]
            self.refreshTableList(fileName, data.tables)

    def onListFileClick(self, item: QTableWidgetItem):
        """点击json table列表"""
        print("Json文件列表 您点击了：" + item.text() + " row:" + str(item.row()))
        # 根据点击事件去看当前选中的item的row，然后找出对应行元素
        jsonItem: QTableWidgetItem = self.ui.listFile.item(item.row(), COL_TB_JSON_NAME)
        fileName: str = jsonItem.data(Qt.UserRole)
        data: ExcelData = self.edata.excelDatas.selectedDatas[fileName]
        tb: TableData = data.tables.get(jsonItem.text())
        # self.treeDialog.show()
        self.treeDialog.refreshTree(tb, self)
        self.syncCheckStateToExcelData()  # 同步勾选状态

    def refreshTableList(self, fileName: str, tables: Dict[str, TableData]):
        """刷新左边表格数据"""
        self.ui.listFile.setRowCount(len(tables))
        count: int = 0
        for tb in tables.values():
            # 插入JSON字段带单选框
            checkBoxJsonName = QTableWidgetItem(tb.jsonName)
            checkBoxJsonName.setData(Qt.UserRole, fileName)
            # 设置单选框为选中
            if tb.isListChecked:
                checkBoxJsonName.setCheckState(Qt.Checked)
            else:
                checkBoxJsonName.setCheckState(Qt.Unchecked)
            # 表名
            itemSheet: QTableWidgetItem = QTableWidgetItem(tb.sheetName)
            # 状态
            itemStatus: QTableWidgetItem = QTableWidgetItem("正常")
            # 最后更新
            itemTime: QTableWidgetItem
            if tb.lastExecuteTime is not None:
                itemTime = QTableWidgetItem(Util.getDateTimeString(tb.lastExecuteTime))
            else:
                itemTime = QTableWidgetItem("")

            self.ui.listFile.setItem(count, COL_TB_JSON_NAME, checkBoxJsonName)
            self.ui.listFile.setItem(count, COL_TB_NAME, itemSheet)
            self.ui.listFile.setItem(count, COL_TB_STATUS, itemStatus)
            self.ui.listFile.setItem(count, COL_TB_LAST_DATE, itemTime)
            count += 1
        self.ui.listFile.setRowCount(count)

    def refreshExcelList(self):
        """刷新右边表格数据"""
        # 开始加载数据
        self.ui.listExcel.setRowCount(len(self.edata.excelDatas.selectedDatas))
        count: int = 0
        for xls in self.edata.excelDatas.selectedDatas.values():
            itemFilename: QTableWidgetItem = QTableWidgetItem(xls.fileName)
            itemCount: QTableWidgetItem = QTableWidgetItem(str(len(xls.tables)))
            if xls.lastExecuteTime is not None:
                itemTime = QTableWidgetItem(Util.getDateTimeString(xls.lastExecuteTime))
            else:
                itemTime = QTableWidgetItem("")

            self.ui.listExcel.setItem(count, COL_XLS_FILENAME, itemFilename)
            self.ui.listExcel.setItem(count, COL_XLS_TABLE_COUNT, itemCount)
            self.ui.listExcel.setItem(count, COL_XLS_LAST_DATE, itemTime)
            count += 1
        self.ui.listExcel.setRowCount(count)

    def syncCheckStateToExcelData(self):
        """同步右边表格勾选状态到记录库"""
        for i in range(0, self.ui.listFile.rowCount()):
            checkboxJsonItem: QTableWidgetItem = self.ui.listFile.item(i, COL_TB_JSON_NAME)
            fileName: str = checkboxJsonItem.data(Qt.UserRole)
            data: ExcelData = self.edata.excelDatas.selectedDatas[fileName]
            if data is None:
                QMessageBox.information(self, "警告", "勾选出错! 没对应文件名")
                continue
            table: TableData = data.tables[checkboxJsonItem.text()]
            if table is None:
                QMessageBox.information(self, "警告", "勾选出错! 没对应表名")
                continue
            if checkboxJsonItem.checkState() == Qt.Checked:
                table.isListChecked = True
            else:
                table.isListChecked = False

    # def onBtnExportClick(self):
    #     """导出选中的excel表"""
    #     exportTime = datetime.datetime.now()
    #     exportVersion = Util.getVersionString(exportTime)  # uuid.uuid1()
    #     jsonGen = JsonGenerator.JsonGenerator()
    #     genPath = self.edata.setting.defDirJsonFile
    #     result = jsonGen.exportFile(genPath, exportVersion, exportTime)
    #     result2 = jsonGen.exportFileSetting(genPath)
    #     if result and result2:
    #         Util.openFilePath(self.edata.setting.defDirJsonFile)
    #         # QMessageBox.information(self, "提示", "已经全部导出为JSON数据表")
    #         self.logger.logAllDone("勾选项目已经全部导出为JSON数据表！(✪ω✪)", self)

    def onBtnSelectAllClick(self):
        """全选"""
        for i in range(0, self.ui.listFile.rowCount()):
            checkbox: QTableWidgetItem = self.ui.listFile.item(i, 0)
            checkbox.setCheckState(Qt.Checked)
        # self.syncCheckStateToExcelData()
        for excelData in self.edata.excelDatas.selectedExcel.datas.values():
            for tableData in excelData.tables.values():
                tableData.isListChecked = True

    def onBtnSelectNoneClick(self):
        """全不选"""
        for i in range(0, self.ui.listFile.rowCount()):
            checkbox: QTableWidgetItem = self.ui.listFile.item(i, 0)
            checkbox.setCheckState(Qt.Unchecked)
        # self.syncCheckStateToExcelData()
        for excelData in self.edata.excelDatas.selectedExcel.datas.values():
            for tableData in excelData.tables.values():
                tableData.isListChecked = False


    def onMenuGiteeClick(self):
        QMessageBox.information(self, "提示",
                                "An open source project\nAuthor:Seraph.yang\nGitee： https://gitee.com/seraph2047/excel2json")

    def setRunningStatus(self, isRunning: bool):
        if isRunning:
            self.setAllButtonsEnabled(self, False)
        else:
            self.setAllButtonsEnabled(self, True)

    def setAllButtonsEnabled(self, obj: QObject, enabled: bool):
        """递归找到按钮操作Enabled"""
        if obj is None:
            return
        for child in obj.children():
            if type(child) is QPushButton:
                button: QPushButton = child
                # print("找到按钮：" + button.text())
                button.setEnabled(enabled)
            else:
                self.setAllButtonsEnabled(child, enabled)

    ####################################################################
    #   【版本Version列表】相关 (倒数第二个列表)
    ####################################################################

    def syncCheckStateVersion(self):
        """同步右边【版本列表】表格勾选状态到记录库"""
        # for i in range(0, self.ui.listVersion.rowCount()):
        #     checkbox: QTableWidgetItem = self.ui.listVersion.item(i, 0)
        #     dirItem: QTableWidgetItem = self.ui.listVersion.item(i, 1)
        #     data: SyncJsonDir = self.edata.setting.syncDirs[dirItem.text()]
        #     if data is None:
        #         QMessageBox.information(self, "警告", "勾选出错! 没对应路径")
        #         continue
        #     if checkbox.checkState() == Qt.Checked:
        #         data.isOpenFolderChecked = True
        #     else:
        #         data.isOpenFolderChecked = False

    def getListJsonData(self, jsonReleasePath: str):
        """根据目录获取json数据包版本信息"""
        defineFilePath: str = jsonReleasePath + self.edata.setting.defDefineJsonFile
        verData = JsonListVersionData()
        verData.isUsable = False
        if not os.path.exists(jsonReleasePath):
            verData.version = "目录错误"
            return verData
        # 尝试读取文件
        jsonData = None
        try:
            with open(defineFilePath, 'r', encoding='utf8') as f:
                jsonData = json.load(f)
        except Exception as e:
            jsonData = None
            self.logger.logError("[版本列表] 打开定义文件_definition.json出错! 路径：" + defineFilePath)

        if jsonData is None:
            verData.version = VERSION_CAN_NOT_FOUND_DATA  # 暂无生成
            return verData
        headData: dict = jsonData.get("header")
        if headData is None:
            verData.version = "文件错误"
            return verData
        verData.version = headData.get("version")
        verData.dateStr = headData.get("date")
        verData.date = Util.getDataTimeFromString(verData.dateStr)
        if verData.date is None:
            verData.date = 0
        verData.author = headData.get("author")
        verData.path = jsonReleasePath
        verData.md5 = headData.get("md5")
        verData.count = Util.parseInt(headData.get("count"), -1)
        verData.isUsable = True
        verData.isDuplicate = False
        return verData

    releaseVerData: JsonListVersionData = None
    """最新版本的数据表"""
    verDataList: List[JsonListVersionData] = None
    """版本列表的数据表集合"""

    def reloadReleaseJsonData(self):
        # json的基础目录
        jsonBasePath: str = self.getGenJsonBasePath()
        if jsonBasePath is None:
            self.logger.logError("找不到数据包Json生成代码目录地址！")
            return False
        # 尝试加载目录
        jsonReleasePath: str = jsonBasePath + self.edata.setting.svnNewestFolder
        data: JsonListVersionData = self.getListJsonData(jsonReleasePath)
        data.tag = VER_TAG_NEWEST  # "本地最新"
        if data.count < 0:
            data.version = VERSION_NEVER_GENERATE_VERSION  # "本地未生成过版本"
        self.releaseVerData = data
        return True

    def reloadListJsonData(self):
        # json的基础目录
        jsonBasePath: str = self.getGenJsonBasePath()
        if jsonBasePath is None:
            self.logger.logError("找不到数据包Json生成代码目录地址！")
            return False

        list: List[JsonListVersionData] = []
        if not self.releaseVerData is None:
            list.append(self.releaseVerData)  # 第一个永远是Release目录

        # 开始读取history目录获取信息
        listHistory: List[JsonListVersionData] = []
        historyBasePath: str = Util.fixPathSlash(jsonBasePath + self.edata.setting.svnHistoryFolder, True)
        # 检查重复
        md5Map: dict[bool] = dict()
        for item in os.listdir(historyBasePath):
            historyFullPath = Util.fixPathSlash(os.path.join(historyBasePath, item), True)
            if os.path.isdir(historyFullPath):
                data: JsonListVersionData = self.getListJsonData(historyFullPath)
                if data.version == VERSION_CAN_NOT_FOUND_DATA:  # 暂无生成
                    continue
                data.tag = "存档"
                isDuplicateMd5 = md5Map.get(data.md5)
                if not isDuplicateMd5 is None:
                    md5Map[data.md5] = True
                else:
                    md5Map[data.md5] = False
                listHistory.append(data)
        # 按时间排序
        sortedList = sorted(listHistory, key=lambda x: x.date, reverse=True)
        # 标记出重复md5的文件夹
        for data in sortedList:
            for (key, isDuplicate) in md5Map.items():
                if key == data.md5 and isDuplicate:
                    data.isDuplicate = True
        # 拼接列表
        list.extend(sortedList)
        self.verDataList = list
        return True

    def refreshTableWidgetJson(self, forceRefreshReleaseVer: bool = False, forceRefreshAll: bool = False):
        """刷新上边【发布目录】表格数据"""
        # 先获取生成目录数据
        if self.releaseVerData is None or forceRefreshReleaseVer or forceRefreshAll:
            if not self.reloadReleaseJsonData():
                self.logger.logError("刷新列表终止！！")
                return

        # 获取列表数据
        if self.verDataList is None or forceRefreshAll:
            if not self.reloadListJsonData():
                self.logger.logError("刷新列表终止！！")
                return

        if forceRefreshReleaseVer and not forceRefreshAll:
            # 如果强制刷新ReleaseVer
            self.verDataList[0] = self.releaseVerData

        self.ui.listVersion.setRowCount(len(self.verDataList))

        count: int = 0
        for ver in self.verDataList:
            print(ver.tag)
            print(ver.date)
            print(ver.version)
            itemTag = QTableWidgetItem(ver.tag)
            diffStr = Util.getChineseTimeDifference(ver.date)
            itemLastDate = QTableWidgetItem(diffStr)
            itemVersion = QTableWidgetItem(ver.version)
            itemCount = QTableWidgetItem(str(ver.count))
            itemAuthor = QTableWidgetItem(ver.author)
            itemMd5 = QTableWidgetItem(ver.md5)
            itemDate = QTableWidgetItem(ver.dateStr)
            # 短路径
            shortPath: str = ver.path.replace(self.edata.currentPath + self.edata.setting.defDirJsonFile, "")
            itemShortPath = QTableWidgetItem(shortPath)
            itemPath = QTableWidgetItem(ver.path)
            if ver.isDuplicate:
                itemTag.setText("重复")
                itemTag.setTextColor(QColor(200, 50, 0))
                itemMd5.setTextColor(QColor(200, 50, 0))
            else:
                itemTag.setTextColor(QColor(50, 200, 0))
            if ver.tag == VER_TAG_NEWEST:  # 最新生成
                itemTag.setBackgroundColor(QColor(230, 255, 230))
                itemLastDate.setBackgroundColor(QColor(230, 255, 230))
                itemVersion.setBackgroundColor(QColor(230, 255, 230))
                itemCount.setBackgroundColor(QColor(230, 255, 230))
                itemAuthor.setBackgroundColor(QColor(230, 255, 230))
                itemMd5.setBackgroundColor(QColor(230, 255, 230))
                itemDate.setBackgroundColor(QColor(230, 255, 230))
                itemShortPath.setBackgroundColor(QColor(230, 255, 230))
                itemPath.setBackgroundColor(QColor(230, 255, 230))

            self.ui.listVersion.setItem(count, COL_VER_TAG, itemTag)
            self.ui.listVersion.setItem(count, COL_VER_LAST_DATE, itemLastDate)
            self.ui.listVersion.setItem(count, COL_VER_VERSION, itemVersion)
            self.ui.listVersion.setItem(count, COL_VER_COUNT, itemCount)
            self.ui.listVersion.setItem(count, COL_VER_AUTHOR, itemAuthor)
            self.ui.listVersion.setItem(count, COL_VER_MD5, itemMd5)
            self.ui.listVersion.setItem(count, COL_VER_DATE, itemDate)
            self.ui.listVersion.setItem(count, COL_VER_SHORT_PATH, itemShortPath)
            self.ui.listVersion.setItem(count, COL_VER_PATH, itemPath)

            btnObj0 = self.ui.listJson.cellWidget(count, COL_VER_OPEN_DIR)
            if not btnObj0 is None:
                # 清理旧按钮，减少内存泄漏
                oldButton: QPushButton = btnObj0
                oldButton.clicked.connect(None)
                del btnObj0

            btnObj1 = self.ui.listJson.cellWidget(count, COL_VER_SYNC)
            if not btnObj1 is None:
                # 清理旧按钮，减少内存泄漏
                oldButton: QPushButton = btnObj1
                oldButton.clicked.connect(None)
                del btnObj1

            btnObj2 = self.ui.listJson.cellWidget(count, COL_VER_PUBLISH)
            if not btnObj2 is None:
                # 清理旧按钮，减少内存泄漏
                oldButton: QPushButton = btnObj2
                oldButton.clicked.connect(None)
                del btnObj2

            btnObj3 = self.ui.listJson.cellWidget(count, COL_VER_SVN)
            if not btnObj3 is None:
                # 清理旧按钮，减少内存泄漏
                oldButton: QPushButton = btnObj3
                oldButton.clicked.connect(None)
                del btnObj3

            if ver.version == VERSION_NEVER_GENERATE_VERSION and ver.count < 0:
                # 如果是“本地未生成”则过
                count += 1
                continue

            # 添加表格[打开目录]按钮功能
            btbView = QPushButton("打开")
            btbView.setProperty("row", count)
            btbView.setProperty("path", ver.path)
            btbView.setFixedSize(50, 24)
            btbView.clicked.connect(self.onJsonDirOpenButtonClick)
            self.ui.listVersion.setCellWidget(count, COL_VER_OPEN_DIR, btbView)

            # 添加表格[拷贝]按钮功能
            btbView = QPushButton("拷贝")
            btbView.setProperty("row", count)
            btbView.setProperty("path", ver.path)
            btbView.setFixedSize(50, 24)
            btbView.clicked.connect(self.onJsonDirSyncButtonClick)
            self.ui.listVersion.setCellWidget(count, COL_VER_SYNC, btbView)

            # 添加表格[更新]按钮功能
            btbView = QPushButton("发布")
            btbView.setProperty("row", count)
            btbView.setProperty("path", ver.path)
            btbView.setFixedSize(50, 24)
            btbView.clicked.connect(self.onJsonDirPublishButtonClick)
            self.ui.listVersion.setCellWidget(count, COL_VER_PUBLISH, btbView)

            # 添加表格[删除]按钮功能
            btbView: QPushButton = None
            if ver.tag == VER_TAG_NEWEST:
                btbView = QPushButton("提交")
                btbView.setStyleSheet('QPushButton {color: #33AA33; background-color: #EEFFEE; font-weight:bold}')
                btbView.clicked.connect(self.onCommitNewestGenerateJsonToSvn)
            else:
                btbView = QPushButton("删除")
                btbView.clicked.connect(self.onJsonDirDeleteButtonClick)

            btbView.setProperty("row", count)
            btbView.setProperty("path", ver.path)
            btbView.setFixedSize(50, 24)
            self.ui.listVersion.setCellWidget(count, COL_VER_SVN, btbView)

            count += 1

        self.ui.listVersion.setRowCount(count)

    def onJsonDirOpenButtonClick(self):
        """打开目录路径"""
        btn: QPushButton = self.sender()
        sourcePath: str = btn.property("path")
        Util.openFilePath(sourcePath)

    def onJsonDirSyncButtonClick(self):
        """同步拷贝文件到目录"""
        btn: QPushButton = self.sender()
        row: int = btn.property("row")
        verItem: QTableWidgetItem = self.ui.listVersion.item(row, COL_VER_VERSION)
        spItem: QTableWidgetItem = self.ui.listVersion.item(row, COL_VER_SHORT_PATH)
        print("row:" + str(row) + " item:" + verItem.text())

        sourcePath: str = btn.property("path")
        choice = QMessageBox.question(self, '确认', '你确定要把：“' + spItem.text() + '”拷贝至【程序资源同步目录】吗？',
                                      QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if choice == QMessageBox.No:
            return

        self.copyJsonDirSync(sourcePath)

    def copyJsonDirSync(self, sourcePath: str):

        # 检测目录是否合理
        if not self.checkJsonFilesDir(sourcePath):
            return
        # 屏蔽按钮可按，防止重复点击
        self.setRunningStatus(True)
        for syncDir in self.edata.setting.syncDirs.values():
            if not Util.isDirAndExists(syncDir.dirPath):
                self.logger.logError("同步拷贝目标目录不存在：" + syncDir.dirPath)
            if not syncDir.isEnabledChecked:  # 不选中启用则不同步
                continue
            # 删除旧文件
            Util.deleteDirectoryFiles(syncDir.dirPath)
            # 拷贝新文件
            self.logger.logSuccess("同步拷贝数据：" + sourcePath + " -> " + syncDir.dirPath)
            Util.copyDirectoryFiles(sourcePath, syncDir.dirPath)
            self.logger.logSuccess("同步拷贝数据：" + syncDir.dirPath + " 完成！")
            if self.edata.setting.chkAutoOpenFolderCopy:
                Util.openFilePath(syncDir.dirPath)

        # 解除屏蔽
        self.setRunningStatus(False)

    def onJsonDirPublishButtonClick(self):
        """发布文件夹至配置中心"""
        btn: QPushButton = self.sender()
        row: int = btn.property("row")
        verItem: QTableWidgetItem = self.ui.listVersion.item(row, COL_VER_VERSION)
        spItem: QTableWidgetItem = self.ui.listVersion.item(row, COL_VER_SHORT_PATH)
        md5Item: QTableWidgetItem = self.ui.listVersion.item(row, COL_VER_MD5)
        print("row:" + str(row) + " item:" + verItem.text())

        path: str = btn.property("path")
        # choice = QMessageBox.question(self, '确认', '你确定要把：“' + spItem.text() + '”发布至【配置中心】吗？', QMessageBox.Yes | QMessageBox.No, QMessageBox.No )
        # if choice == QMessageBox.No:
        #     return
        # 屏蔽按钮可按，防止重复点击
        self.setRunningStatus(True)

        selVer: JsonListVersionData = None
        for ver in self.verDataList:
            if ver.md5 == md5Item.text():
                selVer = ver
                break

        self.publishJson(path, selVer)
        # 解除屏蔽
        self.setRunningStatus(False)

    def onJsonDirDeleteButtonClick(self):
        """删除svn文件夹"""
        btn: QPushButton = self.sender()
        print(btn.property("row"))
        path: str = btn.property("path")

        choice = QMessageBox.question(self, '确认', '你确定要删除SVN上的：“' + path + '”吗？',
                                      QMessageBox.Yes | QMessageBox.No,
                                      QMessageBox.No)
        if choice == QMessageBox.No:
            return
        self.setRunningStatus(True)
        self.logger.logNotice("删除SVN整个文件夹：" + path)
        self.deleteSvnDir(path)
        # 同步svn最新记录下来
        self.onBtnUpdateJsonClick()
        self.setRunningStatus(False)

    def onBtnGenJsonClick(self):
        """按钮: [数据包列表]从xls导入生成新包(只导出勾选)"""
        # 屏蔽按钮可按，防止重复点击
        self.setRunningStatus(True)
        self.doGenJson(False)
        # 解除屏蔽
        self.setRunningStatus(False)

    def onBtnGenJsonAllClick(self):
        """按钮: [数据包列表]从xls导入生成新包(全部)"""
        # 屏蔽按钮可按，防止重复点击
        self.setRunningStatus(True)
        self.doGenJson(True)
        # 解除屏蔽
        self.setRunningStatus(False)

    def doGenJson(self, isGenerateAll: bool):

        # 获取当前时间，并用时间生成当前版本时间与版本号
        exportTime = datetime.datetime.now()
        exportVersion = Util.getVersionString(exportTime)  # uuid.uuid1()

        # svn的Json数据包存放基础地址
        svnUrl: str = self.edata.setting.svnDirJson
        if not svnUrl.endswith("/"):
            svnUrl += "/"
        # 默认发布版本的地址
        newestFullSvnUrl: str = Util.fixUrlBackslash(svnUrl + "/" + self.edata.setting.svnNewestSvnUrl, True)

        # 临时发布版本的目录
        tempSubFolder: str = self.edata.setting.svnTempFolder + "temp_" + exportTime.strftime("%Y-%m-%d_%H-%M-%S")

        # 最后生成版本的目录
        lastGenFolder: str = "last\\"

        # json的基础目录
        jsonBasePath: str = self.getGenJsonBasePath()
        if jsonBasePath is None:
            self.logger.logError("找不到数据包Json生成代码目录地址！")
            return False
        jsonTempPath: str = jsonBasePath + tempSubFolder
        jsonLastGenPath: str = jsonBasePath + self.edata.setting.svnNewestFolder

        # 创建目录
        Util.tryCreateDirectory(jsonTempPath)
        Util.tryCreateDirectory(jsonLastGenPath)

        # 清除临时目录旧文件
        self.cleanTempFolder()

        currentTime = datetime.datetime.now()
        formattedTime = currentTime.strftime('[%H:%M:%S] ')

        # TODO：先把这个每次生成拉取最后版本的限制删除
        # 先拉从svn拉取最后生成版本
        # if not self.checkOutSvn(jsonTempPath, newestFullSvnUrl):
        #     self.logger.logError("无法从SVN拉取之前的数据")
        #     return

        # 创建json数据包生成器（工具）
        jsonGen = JsonGenerator.JsonGenerator()

        # 获取SVN上与本地最后生成的definition版本号
        svnVer: int = jsonGen.getLastVersion(jsonTempPath)
        localVer: int = jsonGen.getLastVersion(jsonLastGenPath)
        isUseLoaclData: bool = localVer > svnVer
        excutePath: str
        if isUseLoaclData:
            # 本地文件版本号比SVN更新，使用本地的为基础
            excutePath = jsonLastGenPath
        else:
            # 使用SVN的为基础生产
            excutePath = jsonTempPath

        # 预读将生成文件目录的旧definition文件
        jsonGen.preloadFileSetting(excutePath)
        # 生成单个json文件
        genCount = jsonGen.exportFile(excutePath, exportVersion, exportTime, isGenerateAll)
        if genCount < 1:  # 生成并没有变化
            self.logger.logNotice("Excel数据表没有任何数据变化")
            # return
        else:
            self.logger.logNotice("Excel数据表本次变化文件个数:" + str(genCount))

        if jsonGen.lastErrorCount > 0:
            self.logger.logWarning(formattedTime + "本次导出错误数据：" + str(jsonGen.lastErrorCount) + "个")

        # print(jsonGen.existTables)
        # 找出不在excel版本管理下的文件
        mustDeleteOldFiles: List[str] = []
        for item in os.listdir(excutePath):
            fn: [str] = os.path.splitext(item)
            filename: str = fn[0]
            ext: str = fn[-1]
            if ext.lower() != ".json":
                continue  # 如果需要判断扩展名但扩展名不对则退出
            if item == self.edata.setting.defDefineJsonFile:  # _definition.json不处理
                continue
            isOldReamin: bool = True
            for table in jsonGen.existTables:
                if table.jsonName.lower() == filename.lower():
                    isOldReamin = False
            if isOldReamin:
                mustDeleteOldFiles.append(item)
        existTables: List[str] = []
        for table in jsonGen.existTables:
            existTables.append(table.jsonName.lower())

        # 生成配置文件
        result2 = jsonGen.exportFileSetting(excutePath, existTables, isGenerateAll)
        if not (result2):
            self.logger.logError(formattedTime + "生成Json数据表出错！")
            return

        # 删除没用的文件
        for delfile in mustDeleteOldFiles:
            # self.deleteSvnFile(delfile, newestFullSvnUrl)
            delfilepath = Util.fixPathSlash(excutePath + "\\" + delfile, False)
            os.chmod(delfilepath, stat.S_IWRITE)  # 文件是只读文件，要删除必须先设置属性
            os.remove(delfilepath)

        self.edata.setting.jsonLastReleasePath = excutePath
        self.logger.logAllDone(formattedTime + "勾选项目已经全部导出为JSON数据表！(✪ω✪)")

        # 如果是使用SVN上的资源则把SVN的表格拷贝到本地最新表格
        if isUseLoaclData == False:
            # 清理旧目录
            self.cleanReleaseFolder()
            # 拷贝到rlease目录 --为了防止svn冲突，放release的不直接是svn目录
            Util.copyDirectoryFiles(excutePath, jsonLastGenPath)

        if self.edata.setting.chkZip:  # 如果勾选了压缩，则自动压缩
            fileName = self.edata.setting.genZipFile
            with zipfile.ZipFile(jsonLastGenPath + '\\' + fileName, 'w', zipfile.ZIP_DEFLATED) as zipf:
                for root, _, files in os.walk(jsonLastGenPath):
                    for file in files:
                        if file.endswith('.json'):  # 只压缩以 .json 结尾的文件
                            file_path = os.path.join(root, file)
                            zipf.write(file_path, os.path.relpath(file_path, jsonLastGenPath))


        self.refreshTableWidgetJson(forceRefreshReleaseVer=True)  # 刷新table更新状态

        if self.edata.setting.chkAutoOpenFolderGenJson:  # 如果勾了才自动打开生成目录
            Util.openFilePath(jsonLastGenPath)

        if self.edata.setting.chkAutoCopyGenJson:
            self.copyJsonDirSync(jsonLastGenPath)

    def onCommitNewestGenerateJsonToSvn(self):
        """按钮: [数据包列表]提交最新生成的数据包到SVN"""
        # 先调用同步svn最新记录下来
        self.onBtnUpdateJsonClick()
        listSize = len(self.verDataList)
        if listSize < 1:
            self.logger.logError("没有版本可以提交，请先生成数据包！")
            return
        baseVer = self.verDataList[0]
        for i in range(1, listSize):
            cmpVer = self.verDataList[i]
            if cmpVer.md5 == baseVer.md5:
                self.logger.logError("最新数据包与历史数据包内容一致（CRC校验），无需提交！")
                return

        # 只能提交本次生产(第一个)
        jsonBasePath: str = self.getGenJsonBasePath()
        sourcePath: str = jsonBasePath + self.edata.setting.svnNewestFolder
        # 检测目录是否合理
        if not self.checkJsonFilesDir(sourcePath):
            return
        # 屏蔽按钮可按，防止重复点击
        self.setRunningStatus(True)
        # 临时发布版本的目录
        tempTime = datetime.datetime.now()
        historyBasePath: str = jsonBasePath + self.edata.setting.svnHistoryFolder
        historySubFolder: str = "save_" + tempTime.strftime("%Y-%m-%d_%H-%M-%S")
        historyFullPath: str = historyBasePath + historySubFolder
        tempBasePath: str = jsonBasePath + self.edata.setting.svnTempFolder
        tempFullPath: str = tempBasePath + historySubFolder
        newestTempFullPath: str = tempBasePath + "newest_" + tempTime.strftime("%Y-%m-%d_%H-%M-%S")

        # 先拉从svn拉取最后生成版本
        svnUrl: str = self.edata.setting.svnDirJson  # svn的Json数据包存放基础地址
        historyFullSvnUrl: str = Util.fixUrlBackslash(
            svnUrl + "/" + self.edata.setting.svnHistorySvnUrl + "/" + historySubFolder, True)
        newestFullSvnUrl: str = Util.fixUrlBackslash(svnUrl + "/" + self.edata.setting.svnNewestSvnUrl, True)

        Util.tryCreateDirectory(historyFullPath)
        Util.tryCreateDirectory(tempFullPath)
        Util.tryCreateDirectory(newestTempFullPath)

        # 提交至历史svn目录
        self.cleanTempFolder()
        if not self.checkOutSvn(tempFullPath, historyFullSvnUrl):
            self.logger.logError("无法从SVN拉取之前的数据")
            return

        Util.copyDirectoryFiles(sourcePath, historyFullPath)
        Util.copyDirectoryFiles(sourcePath, tempFullPath)
        self.commitSvn(tempFullPath, historyFullSvnUrl, ".json")
        self.logger.logAllDone("提交至历史记录SVN完毕！")

        # 提交至最新版本svn目录
        if not self.checkOutSvn(newestTempFullPath, newestFullSvnUrl):
            self.logger.logError("无法从SVN拉取之前的数据")
            return

        # 找出本地不存在的文件
        commitFiles: List[str] = []
        for item in os.listdir(sourcePath):
            fn: [str] = os.path.splitext(item)
            filename: str = fn[0]
            ext: str = fn[-1]
            if ext.lower() != ".json":
                continue  # 如果需要判断扩展名但扩展名不对则退出
            if item == self.edata.setting.defDefineJsonFile:  # _definition.json不处理
                continue
            commitFiles.append(item)

        mustDeleteOldFiles: List[str] = []
        for item in os.listdir(newestTempFullPath):
            ext: str = os.path.splitext(item)[-1]
            if ext.lower() != ".json":
                continue  # 如果需要判断扩展名但扩展名不对则退出
            if item == self.edata.setting.defDefineJsonFile:  # _definition.json不处理
                continue
            isOldReamin: bool = True
            for commiteFile in commitFiles:
                if commiteFile.lower() == item.lower():
                    isOldReamin = False
            if isOldReamin:
                mustDeleteOldFiles.append(item)

        # 正式删除过期的文件（含SVN）
        for delfile in mustDeleteOldFiles:
            self.deleteSvnFile(delfile, newestFullSvnUrl)
            delfilepath = Util.fixPathSlash(newestTempFullPath + "\\" + delfile, False)
            os.chmod(delfilepath, stat.S_IWRITE)  # 文件是只读文件，要删除必须先设置属性
            os.remove(delfilepath)

        Util.copyDirectoryFiles(sourcePath, newestTempFullPath)
        self.commitSvn(newestTempFullPath, newestFullSvnUrl, ".json")
        self.logger.logAllDone("提交至最新版本记录SVN完毕！")

        self.refreshTableWidgetJson(forceRefreshAll=True)  # 刷新table更新状态
        # 解除屏蔽
        self.setRunningStatus(False)

    def checkAndLoggerHttpResult(self, result: dict):
        """打印网络返回包"""
        if result is None:
            self.logger.logError("网络数据获取失败！")
            return Publish.UPLOAD_COMM_RESULT_ERROR
        objRsult = result.get("result")
        if objRsult is None:
            self.logger.logError("服务器内部错误！")
            self.logger.logWarning(str(result))
            return Publish.UPLOAD_COMM_RESULT_ERROR
        resultCode: int = Util.parseInt(objRsult, Publish.UPLOAD_COMM_RESULT_ERROR)
        if resultCode == Publish.UPLOAD_COMM_RESULT_ERROR:
            self.logger.logError("服务器内部错误！")
            return resultCode
        elif resultCode == Publish.LOGIN_RESULT_USER_OR_PASS_WRONG:
            self.logger.logError("登录服务器失败：账号或密码错误！")
            return resultCode
        elif resultCode == Publish.UPLOAD_COMM_ERROR_FORMAT:
            self.logger.logError("上传数据格式错误")
            return resultCode
        elif resultCode == Publish.UPLOAD_COMM_ERROR_EMPTY:
            self.logger.logError("上传数据为空")
            return resultCode
        elif resultCode == Publish.UPLOAD_COMM_ERROR_TOKEN_NOT_FOUND:
            self.logger.logError("传输握手已经过期")
            return resultCode
        elif resultCode == Publish.PUBLISH_RESULT_ERROR:
            self.logger.logError("发布取消！服务器内部错误")
            return resultCode
        elif resultCode == Publish.PUBLISH_RESULT_ERROR_WRONG_DEFINTION:
            self.logger.logError("发布取消！上传的定义数据definition有误！")
            return resultCode
        elif resultCode == Publish.PUBLISH_RESULT_ERROR_WRONG_TABLE:
            self.logger.logError("发布取消！上传含有错误的表格数据")
            return resultCode

        return resultCode

    # def onBtnPublishJsonClick(self):
    #     """按钮: [数据包列表]发布选中数据包"""
    #     currentSelectedPath: str = self.getSelectedItemPath()
    #     if currentSelectedPath is None:
    #         return
    #     # 屏蔽按钮可按，防止重复点击
    #     self.setRunningStatus(True)
    #     self.publishJson(currentSelectedPath)
    #     # 解除屏蔽
    #     self.setRunningStatus(False)

    def publishJson(self, currentSelectedPath: str, selVer: JsonListVersionData):
        if not Util.isDirAndExists(currentSelectedPath):
            QMessageBox.warning(self, "提示", "选择的数据包并没有数据")
            return
        definition: ExcelLibDefinitionMain = Util.getDefinition(currentSelectedPath)
        if definition is None:
            self.logger.logError("你所选的不是有效数据包：" + currentSelectedPath)
            return

        caption: str
        host: str = self.edata.setting.cfgServerUrl
        username: str = self.edata.setting.cfgServerUser
        passcode: str = self.edata.setting.cfgServerPwd

        if selVer is None:
            self.logger.logWarning("你选择的数据包版本的没有记录！")
        self.logger.logNotice("查看[" + selVer.version + "]明细")

        self.publishDialog.show(currentSelectedPath, selVer)
        return

        # 弹出选择服务器
        items: [] = []
        for serverKey in self.edata.setting.syncServers.keys():
            key: str = serverKey
            items.append(key)

        selectKey, ok = QInputDialog.getItem(self, "发布", "请选择你发布的服务器：", items, 0, False)
        if not ok:
            return
        print(selectKey, ok)

        selectedItem: SyncPublishServer = None
        for key, value in self.edata.setting.syncServers.items():
            if key == selectKey:
                selectedItem = value

        if selectedItem == None:
            QMessageBox.warning(self, "提示", "选择的服务器有误")
            return

        caption = selectedItem.caption
        host = selectedItem.serverPath
        tag = selectedItem.tag
        username = selectedItem.user
        passcode = selectedItem.pwd
        print("选择的服务器是：" + caption + " host:" + host + " Tag:" + tag + " user:" + username + " pwd:" + passcode)

        # 登陆发布服务器
        loginResult: dict = Publish.loginConfigServer(host, tag, username, passcode)  # 临时账号密码
        # 检查返回内容并输出错误
        if self.checkAndLoggerHttpResult(loginResult) < 0:
            return
        token: str = loginResult["token"]
        jsonData: {} = None

        definitionFileName = self.edata.setting.defDefineJsonFile
        definePath = os.path.join(currentSelectedPath, definitionFileName)
        result = None
        if os.path.isfile(definePath):
            jsonText: str = None
            with open(definePath, 'r', encoding='utf8') as f:
                jsonText = f.read()
            if jsonText is None:
                self.logger.logError("文件读取错误：" + definePath)
            else:
                result = Publish.postDefinition(host, tag, token, definition.version, jsonText)
                # 检查返回内容并输出错误
                if self.checkAndLoggerHttpResult(result) < 0:
                    self.logger.logError("definition.json提交失败！")
                    return
        else:
            self.logger.logError("definition.json数据文件不存在！")
            return

        for item in os.listdir(currentSelectedPath):
            tableDefine: ExcelLibDefinitionTable = None
            for table in definition.tables:
                if table.jsonName + ".json" == item:
                    tableDefine = table
            if tableDefine is None:
                continue
            jsonPath = os.path.join(currentSelectedPath, item)
            if os.path.isfile(jsonPath):  # 只拷贝文件
                if item == self.edata.setting.defDefineJsonFile:
                    # definition.json不提交
                    continue
                jsonText: str = None
                with open(jsonPath, 'r', encoding='utf8') as f:
                    jsonText = f.read()
                if jsonText is None:
                    self.logger.logError("文件读取错误：" + jsonPath)
                else:
                    result = Publish.postTable(host, tag, token, item, definition.version, jsonText)
                    # 检查返回内容并输出错误
                    if self.checkAndLoggerHttpResult(result) < 0:
                        return

                self.logger.logNotice("提交服务器表数据文件：" + item)

        result = Publish.publishData(host, tag, token, 1)
        if self.checkAndLoggerHttpResult(result) < 0:
            return
        self.logger.logAllDone("提交服务器表数据完成")

        # 发布后验证数据
        chechResult: dict = Publish.getLastVersion(host, tag)
        if chechResult is None:
            self.logger.logWarning("复检错误！请手动检查上传后数据")
            return
        version = chechResult.get("version")
        author = chechResult.get("author")
        md5 = chechResult.get("md5")
        count = chechResult.get("count")
        self.logger.logNotice("复检结果，当前线上服务器表数据：")
        self.logger.logNotice("版本：" + version)
        self.logger.logNotice("CRC：" + md5)
        self.logger.logNotice("发布者：" + author)
        self.logger.logNotice("数据表总数：" + str(count))

    def onBtnUpdateJsonClick(self):
        """按钮: [数据包列表]从SVN同步新包"""
        # 屏蔽按钮可按，防止重复点击
        self.setRunningStatus(True)
        self.updateJsonFromSvn()
        # 解除屏蔽
        self.setRunningStatus(False)

    def updateJsonFromSvn(self):
        """按钮: [数据包列表]从SVN同步新包"""
        # 先算好历史记录的本地地址与svn地址
        jsonBasePath: str = self.getGenJsonBasePath()
        remoteHistoryBasePath: str = jsonBasePath + self.edata.setting.svnRemoteHistoryFolder
        historySvnUrl: str = self.edata.setting.svnHistorySvnUrl
        svnUrl: str = self.edata.setting.svnDirJson
        historyBaseSvnUrl: str = Util.fixUrlBackslash(svnUrl + "/" + historySvnUrl + "/", True)
        # 没有目录则创建
        if not Util.isDirAndExists(remoteHistoryBasePath):
            Util.tryCreateDirectory(remoteHistoryBasePath)

        self.checkOutSvn(remoteHistoryBasePath, historyBaseSvnUrl)
        self.logger.logNotice("开始从临时目录同步数据至本地目录")
        # 先把本地的删除
        historyBasePath: str = jsonBasePath + self.edata.setting.svnHistoryFolder
        Util.deleteDirectoryFiles(historyBasePath, True)
        # 把svn的拷贝到同步目录
        Util.copyDirectoryFiles(remoteHistoryBasePath, historyBasePath, True, [".svn"])
        self.logger.logAllDone("从SVN同步最新列表完成！")

        self.refreshTableWidgetJson(forceRefreshAll=True)

    ####################################################################
    #   【发布列表】相关 最下方
    ####################################################################

    def onListVersionClick(self, item: QTableWidgetItem):
        """点击右边【版本列表】表格数据"""
        print("【版本列表】QTable 您点击了：" + item.text() + " row:" + str(item.row()))
        # self.syncCheckStateVersion()  # 同步勾选状态
        verItem: QTableWidgetItem = self.ui.listVersion.item(item.row(), COL_VER_MD5)
        selVer: JsonListVersionData = None
        for ver in self.verDataList:
            if ver.md5 == verItem.text():
                selVer = ver
                break
        if selVer is None:
            self.logger.logWarning("你选择的数据包版本的没有记录！")
        self.logger.logNotice("查看[" + selVer.version + "]明细")

        self.refreshListFile(selVer)

    def refreshListFile(self, ver: JsonListVersionData):
        """刷新左边【发布目录】表格数据"""
        if not Util.isDirAndExists(ver.path):
            self.logger.logError("你选择的数据包版本目录数据错误！")
            return

        definitionFn: str = ver.path + self.edata.setting.defDefineJsonFile
        if not Util.isFileAndExists(definitionFn):
            self.logger.logError("你选择的数据包_definition.json数据错误！")
            return
        with open(definitionFn, 'r', encoding='utf8') as f:
            defineJsonData = json.load(f)
        if defineJsonData is None:
            self.logger.logError("你选择的数据包_definition.json数据读取错误！")
            return
        headData: dict = defineJsonData.get("header")
        if headData is None:
            self.logger.logError("你选择的数据包_definition.json数据没有Head数据！")
            return

        dfVersion = headData["version"]
        dfDate = headData["date"]
        dfAuthor = headData["author"]

        loadconfigs: [] = defineJsonData.get("defines")

        tablesData: List[TableData] = None
        tmpTablesData = defineJsonData.get("tables")
        if type(tmpTablesData) is list:
            tablesData = tmpTablesData

        fileList: List[str] = os.listdir(ver.path)
        self.ui.listJson.setRowCount(len(fileList))
        count: int = 0
        for fn in fileList:
            if fn == self.edata.setting.defDefineJsonFile:  # 不显示definition文件
                continue
            if fn == ".svn":  # 不显示 .svn目录
                continue
            fPath = os.path.join(ver.path, fn)
            jsonName: str = os.path.splitext(fn)[0]
            itemTag = QTableWidgetItem("正常")
            itemFileName = QTableWidgetItem(fn)
            md5: str = ""
            author: str = ""
            strTime: str = ""
            for i in range(0, len(tablesData)):
                oldTbData = tablesData[i]
                if oldTbData["jsonName"] == jsonName:
                    md5 = oldTbData["md5"]
                    author = oldTbData["author"]
                    strTime = oldTbData["date"]

            itemMd5 = QTableWidgetItem(md5)
            itemAuthor = QTableWidgetItem(author)
            itemTime = QTableWidgetItem(strTime)

            self.ui.listJson.setItem(count, COL_JSON_TAG, itemTag)
            self.ui.listJson.setItem(count, COL_JSON_FILENAME, itemFileName)
            self.ui.listJson.setItem(count, COL_JSON_AUTHOR, itemAuthor)
            self.ui.listJson.setItem(count, COL_JSON_MD5, itemMd5)
            self.ui.listJson.setItem(count, COL_JSON_DATE, itemTime)

            # 添加表格[查看]按钮功能
            btnObj1 = self.ui.listJson.cellWidget(count, COL_JSON_FUN_VIEW)
            if not btnObj1 is None:
                # 清理旧按钮，减少内存泄漏
                oldButton: QPushButton = btnObj1
                oldButton.clicked.connect(None)
                del btnObj1
            btbView = QPushButton("查看")
            btbView.setProperty("row", count)
            btbView.setProperty("path", ver.path)
            btbView.setProperty("fn", fn)
            btbView.setFixedSize(50, 24)
            btbView.clicked.connect(self.onJsonViewButtonClick)
            self.ui.listJson.setCellWidget(count, COL_JSON_FUN_VIEW, btbView)

            # 添加表格[比较]按钮功能
            btnObj2 = self.ui.listJson.cellWidget(count, COL_JSON_FUN_COMPARE)
            if not btnObj2 is None:
                # 清理旧按钮，减少内存泄漏
                oldButton: QPushButton = btnObj2
                oldButton.clicked.connect(None)
                del btnObj2
            btbCmp = QPushButton("比较")
            btbCmp.setProperty("row", count)
            btbCmp.setProperty("path", ver.path)
            btbCmp.setProperty("fn", fn)
            btbCmp.setFixedSize(50, 24)
            btbCmp.clicked.connect(self.onJsonCompareButtonClick)
            self.ui.listJson.setCellWidget(count, COL_JSON_FUN_COMPARE, btbCmp)

            # 添加表格[编辑]按钮功能
            btnObj3 = self.ui.listJson.cellWidget(count, COL_JSON_FUN_EDIT)
            if not btnObj3 is None:
                # 清理旧按钮，减少内存泄漏
                oldButton: QPushButton = btnObj3
                oldButton.clicked.connect(None)
                del btnObj3
            btbCmp = QPushButton("编辑")
            btbCmp.setProperty("row", count)
            btbCmp.setProperty("path", ver.path)
            btbCmp.setProperty("fn", fn)
            btbCmp.setFixedSize(50, 24)
            btbCmp.clicked.connect(self.onJsonEditButtonClick)
            self.ui.listJson.setCellWidget(count, COL_JSON_FUN_EDIT, btbCmp)

            # 添加表格[删除]按钮功能
            btnObj4 = self.ui.listJson.cellWidget(count, COL_JSON_FUN_DELETE)
            if not btnObj4 is None:
                # 清理旧按钮，减少内存泄漏
                oldButton: QPushButton = btnObj4
                oldButton.clicked.connect(None)
                del btnObj4
            btbCmp = QPushButton("删除")
            btbCmp.setProperty("row", count)
            btbCmp.setProperty("path", ver.path)
            btbCmp.setProperty("fn", fn)
            btbCmp.setFixedSize(50, 24)
            btbCmp.clicked.connect(self.onJsonDeleteButtonClick)
            self.ui.listJson.setCellWidget(count, COL_JSON_FUN_DELETE, btbCmp)

            count += 1
        self.ui.listJson.setRowCount(count)

    def onJsonEditButtonClick(self):
        """点击查看json文件内容"""
        print(self.sender())
        btn: QPushButton = self.sender()
        print(btn.property("row"))
        path: str = btn.property("path")
        fn: str = btn.property("fn")
        print(path, fn)
        win32api.ShellExecute(0, 'open', path + fn, '', '', 1)

    def onJsonDeleteButtonClick(self):
        """点击查看json文件内容"""
        print(self.sender())
        btn: QPushButton = self.sender()
        print(btn.property("row"))
        path: str = btn.property("path")
        fn: str = btn.property("fn")
        # self.deleteSvn(fn, path)

    def onJsonViewButtonClick(self):
        """点击查看json文件内容"""
        print(self.sender())
        btn: QPushButton = self.sender()
        print(btn.property("row"))
        path: str = btn.property("path")
        fn: str = btn.property("fn")
        self.viewerDialog.show()
        self.viewerDialog.viewJson(path, fn)

    def onJsonCompareButtonClick(self):
        btn: QPushButton = self.sender()
        path: str = btn.property("path")
        fn: str = btn.property("fn")
        itemMap: dict = dict();
        items: [] = []
        key: str = "最新生成：" + "[" + self.releaseVerData.version + "] "
        itemMap[key] = self.releaseVerData.path
        items.append(key)
        currentVersion: str = ""
        for ver in self.verDataList:
            diffStr: str = Util.getChineseTimeDifference(ver.date)
            key: str = "[" + ver.tag + "][" + ver.version + "] " + diffStr + " (" + ver.author + ")"
            if ver.path == path:
                currentVersion = key
                continue
            itemMap[key] = ver.path
            items.append(key)

        item, ok = QInputDialog.getItem(self, "文件比较", "请选择你要比对的版本，（如果该版本存在相同文件即可比对）\n"
                                        + "你当前版本是：\n\n"
                                        + currentVersion, items, 0, False)
        if not ok:
            return
        print(item, ok)
        pathDiff = itemMap[item]
        print(pathDiff)
        self.viewerDialog.show()
        self.viewerDialog.diffJson(path, pathDiff, fn)

    def onListJsonClick(self, item: QTableWidgetItem):
        """点击左边【发布目录】表格数据"""
        print("【Json表】您点击了：" + item.text() + " row:" + str(item.row()))
        specifyRow = self.ui.listJson.currentRow()
        if specifyRow > self.ui.listJson.rowCount() - 1:
            QMessageBox.warning(self, "提示", "选择的数据包不在范围内！")
            return None
        dirItem: QTableWidgetItem = self.ui.listJson.item(specifyRow, COL_JSON_DATE)

    #########################
    # 【工具类】函数
    #########################

    def checkJsonFilesDir(self, currentSelectedPath: str):
        """
        检测json目录路径是否正常可用
        :param currentSelectedPath: json数据绝对路径
        :return: 是否正常
        """
        if currentSelectedPath is None:
            return False
        if not Util.isDirAndExists(currentSelectedPath):
            QMessageBox.warning(self, "提示", "选择的数据包并没有数据")
            return False
        jsonBasePath: str = self.getGenJsonBasePath()
        if jsonBasePath is None:
            self.logger.logError("找不到数据包Json生成代码目录地址！")
            return False
        # 尝试加载目录
        selectedData: JsonListVersionData = self.getListJsonData(currentSelectedPath)
        if not selectedData.isUsable:
            self.logger.logError("数据包数据错误，取消执行！")
            return False
        return True

    def getSelectedItemPath(self, specifyRow: int = -1):
        """
        获取版本列表中选定的一行的数据
        :param specifyRow: 指定第几行，默认不指定
        :return:
        """
        if specifyRow < 0:
            if self.ui.listVersion.currentRow() < 0:
                QMessageBox.warning(self, "提示", "你还没有选择要操作数据包！")
                return None
            specifyRow = self.ui.listVersion.currentRow()
        if specifyRow > self.ui.listVersion.rowCount() - 1:
            QMessageBox.warning(self, "提示", "选择的数据包不在范围内！")
            return None
        dirItem: QTableWidgetItem = self.ui.listVersion.item(specifyRow, COL_VER_PATH)
        return dirItem.text()

    def cleanReleaseFolder(self):
        """清理release版本目录旧文件"""
        jsonBasePath: str = self.getGenJsonBasePath()
        if jsonBasePath is None:
            self.logger.logError("找不到数据包Json生成代码目录地址！")
            return False
        jsonReleasePath: str = jsonBasePath + self.edata.setting.svnNewestFolder
        Util.deleteDirectoryFiles(jsonReleasePath, True, "*.*")

    def cleanTempFolder(self):
        """
        清理生成目录
        :return:
        """
        jsonTempPath: str = self.getGenJsonBasePath() + self.edata.setting.svnTempFolder
        self.logger.logNotice("删除临时文件夹：" + jsonTempPath)
        Util.deleteDirectoryFiles(jsonTempPath, True, "*.*")

    def getGenJsonBasePath(self):
        """获得laya Vo生成基础目录，结尾带'\' """
        dirPath: str = Util.getCodePath(self.edata.currentPath, self.edata.setting.defDirJsonFile)
        return dirPath

    def deleteSvnFile(self, filename: str, svnDir: str):
        """
        删除svn上的一个文件
        :param filename: 文件名
        :param fileDir: 删除文件对应的本地目录（反推svn地址）
        :return:是否删除成功
        """
        # self.logger.logSpace()  # 空行
        svnUrl: str = Util.fixUrlBackslash(self.edata.setting.svnUrl + "/" + svnDir, True)
        # delfileUrl: str = Util.fixUrlBackslash(svnUrl + "/" + filename, False)
        self.logger.logNotice("开始删除'" + svnUrl + filename + "'SVN文件...")
        SvnTool.svnDelete(self.logger,
                          svnUrl,
                          filename,
                          self.edata.setting.svnUser,
                          self.edata.setting.svnPwd)

    def deleteSvnDir(self, fileDir: str):
        """
        删除svn上的一个文件
        :param filename: 文件名
        :param fileDir: 删除文件对应的本地目录（反推svn地址）
        :return:是否删除成功
        """
        self.logger.logSpace()  # 空行
        svnPath: str = fileDir.replace(self.edata.currentPath + self.edata.setting.defDirJsonFile, "")
        svnBaseUrl: str = Util.fixUrlBackslash(self.edata.setting.svnUrl, True)
        svnUrl: str = Util.fixUrlBackslash(self.edata.setting.svnDirJson + "/" + svnPath, True)
        self.logger.logNotice("准备删除：'" + svnUrl)

        SvnTool.svnDelete(self.logger,
                          svnBaseUrl,
                          svnUrl,
                          self.edata.setting.svnUser,
                          self.edata.setting.svnPwd)

    #     self.logger.logNotice("准备删除，先更新'" + fileDir + "'SVN目录至最新（防止SVN冲突）...")
    #     # SVN检出文件
    #     svnJavaTempDir = SvnTool.checkoutSvnDir(self.logger,
    #                                             fileDir,
    #                                             svnUrl,
    #                                             self.edata.setting.svnUser,
    #                                             self.edata.setting.svnPwd)
    #     self.logger.logNotice("开始删除'" + svnUrl + "'SVN文件...")
    #

    def checkOutSvn(self, genFolder: str, svnPath: str):
        """
        检出svn
        :param genVoBasePath: 生成对应代码VO的地址
        :param tempFolder: 动态创建临时目录
        :param svnPath: svn上对应的目录
        :return:
        """
        # Java
        self.logger.logSpace()  # 空行
        self.logger.logNotice("开始检出'" + svnPath + "'SVN...")
        # SVN尝试创建目录
        svnUrl: str = self.edata.setting.svnUrl + svnPath
        SvnTool.svnMkdir(self.logger,
                         svnUrl,
                         self.edata.setting.svnUser,
                         self.edata.setting.svnPwd)
        # SVN检出文件
        svnJavaTempDir = SvnTool.checkoutSvnDir(self.logger,
                                                genFolder,
                                                svnUrl,
                                                self.edata.setting.svnUser,
                                                self.edata.setting.svnPwd)
        self.logger.logAllDone("检出'" + svnPath + "'SVN完毕...")
        return True

    def commitSvn(self, srcDirPath: str, svnPath: str, extFileName: str):
        """
        提交svn
        :param srcDirPath: 代码生成目录，需要提交svn的目录
        :return:
        """
        self.logger.logSpace()  # 空行
        self.logger.logNotice("开始提交'" + svnPath + "'SVN...")
        if not srcDirPath.endswith("\\"):
            srcDirPath += "\\"
        files: List[str] = []

        for item in os.listdir(srcDirPath):
            fileFullPath = os.path.join(srcDirPath, item)
            if not os.path.isfile(fileFullPath):  # 只处理文件
                continue
            if extFileName.find("*") < 0:
                ext: str = os.path.splitext(fileFullPath)[-1]
                if ext.lower() != extFileName.lower():
                    continue
            files.append(fileFullPath)

        self.logger.logNotice("[SVN][Commit] 开始提交'" + svnPath + "' Json数据包...")
        SvnTool.commitSVNFiles(self.logger,
                               srcDirPath,
                               self.edata.setting.svnUrl + svnPath,
                               files,
                               self.edata.setting.svnUser,
                               self.edata.setting.svnPwd)
        self.logger.logNotice("[SVN][Commit] 提交'" + svnPath + "' Json数据包结束")
        self.logger.logAllDone("提交SVN完毕...")


if __name__ == '__main__':
    app = QApplication([])
    mainForm = Excel2JsonWin()
    mainForm.show()
    sys.exit(app.exec_())
